import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Stack;
import java.util.ArrayList;
import java.util.EmptyStackException;
import java.lang.reflect.Method;

public class Calc{
  public static InputStreamReader i = new InputStreamReader(System.in);
  public static BufferedReader in = new BufferedReader(i);
  public static Stack<Object> stack = new Stack<Object>();

  public static void main(String[] args){
    String res;
    int i = 1;

    CalcFuncs.set(":postfix-mode", new CalcBool(true));
    CalcFuncs.set(":round-length", ILN.neu("20"));

    do {
      // prompt
      System.out.printf("calc:%03d> ", i);
      res = gets();

      // quit on: ^D, exit, quit
      if (res == null) break;
      res = res.trim();
      if (res.equals("exit") || res.equals("quit")) break;

      // help
      if (res.equals("help")){
        stack.push("usage");
        simp();
      }

      if (res.equals("")){
        i++;
        continue;
      }

      String[] resu = res.split("\\s+");
      ArrayList<String> results = new ArrayList<String>(resu.length);
      for (int j = 0; j < resu.length; j++){
        if (resu[j].charAt(0) == '"'){
          String first = resu[j].substring(1);
          if (first.charAt(first.length() - 1) == '"'){
            results.add("\"" + first);
            continue;
          }
          try {
            do {
              j++;
              first += " " + resu[j];
            } while (resu[j].charAt(resu[j].length() - 1) != '"');
            results.add("\"" + first);
          }
          catch (Exception ex){
            puts("Input lacks a ending quote");
          }
          continue;
        }
        results.add(resu[j]);
      }

      if (!((CalcBool)CalcFuncs.get(":postfix-mode")).bool){
        ArrayList<String> tmpres = new ArrayList<String>(results.size());
        for (int k = results.size() - 1; k >= 0; k--){
          tmpres.add(results.get(k));
        }
        results = tmpres;
      }

      for (String re : results) {
        if (re.matches("^[ep!%/\\*\\&\\|\\^\\~=]|==") || re.equals("+") ||
            re.equals("-")){
          switch (re.charAt(0)){
            case '+': stack.push("plus"); break;
            case '-': stack.push("minus"); break;
            case '*': stack.push("times"); break;
            case '/': stack.push("over"); break;
            case '%': stack.push("modulo"); break;
            case '!':
              if (stack.size() <= 1)
                puts("Not enough elements on stack!");
              else{
                Object one = stack.pop();
                Object two = stack.pop();
                stack.push(one);
                stack.push(two);
              }
              break;
            case 'e': while (!stack.empty()) stack.pop(); break;
            case 'p': if (!stack.empty()) stack.pop(); break;
            case '&': stack.push("and"); break;
            case '|': stack.push("or"); break;
            case '^': stack.push("xor"); break;
            case '~': stack.push("not"); break;
            case '=':
              if (re.length() > 1 && re.charAt(1) == '=')
                stack.push("equals");
              else
                stack.push("set");
              break;
          }
          simp();
        }
        if (re.matches("[\\+\\-]?\\d+(\\.\\d*)?(e\\-?\\d+)?")){   // handle numbers
          stack.push(ILN.neu(re));
        }
        else if (re.equals("if")){
          stack.push("iff");
          simp();
        }
        else if (re.charAt(0) == ':'){    // handle functions
          stack.push(re.substring(1));
          simp();
        }
        else if (re.charAt(0) == '\''){   // handle strings
          stack.push(new CalcString(re.substring(1)));
        }
        else if (re.charAt(0) == '"'){    // handle strings
          stack.push(new CalcString(re.substring(1, re.length() - 1)));
        }
        else if (re.charAt(0) == '?'){    // handle bools
          stack.push(new CalcBool(re.toLowerCase().equals("?true")));
        }
      }

      puts("=> " + stack);
      i++;
    }
    while (true);
    if (res == null) puts("");
  }

  public static void simp(){
    try {
      simplifyStack();
    }
    catch (EmptyStackException e){
      puts("Not enough elements on stack!");
    }
    catch (Exception e){
      e.printStackTrace();
    }
  }

  public static void simplifyStack(){
    if (stack.empty()) return;
    Object top = stack.peek();
    if (top.getClass() == "".getClass()){   // function
      stack.pop();  // pop the function off the stack
      ArrayList<Method> cbs = new ArrayList<Method>();
      Method cb = null;
      for (Method m : CalcFuncs.class.getDeclaredMethods())
        if (m.getName().equals(top)) cbs.add(m);
      
      boolean matches = false;
      for (int i = 0; i < cbs.size(); i++){
        if (matchStack(cbs.get(i), top.equals("set") ||
              top.equals("has") || top.equals("del") ||
              top.equals("get"))){
          cb = cbs.get(i);
          matches = true;
        }
      }

      if (cbs.size() != 0 && !matches){
        puts("" + cbs);
        puts("" + stack.peek().getClass());
        throw new RuntimeException("The function exists, but does " +
            "not have a signature matching the stack.");
      }

      if (cb == null){
        stack.push(((((String)top).charAt(0) == ':')? "" : ":") + top);
        return;
      }

      int paramcount = cb.getParameterTypes().length;
      Object[] params = new Object[paramcount];
      for (int i = 0; i < paramcount; i++)
        params[i] = stack.pop();

      Object res = null;
      try {
        res = cb.getReturnType().cast(cb.invoke(null, params));
      }
      catch (Exception ex){
        ex.printStackTrace();
      }

      if (res != null && !cb.getReturnType().getName().equals("void"))
        stack.push(res);

      return;
    }
  }

  public static boolean matchStack(Method m, boolean igStr){
    Class[] mPar = m.getParameterTypes();
    if (mPar.length > stack.size()) return false;
    Stack<Object> tmp = new Stack<Object>();

    for (int i = 0; i < mPar.length; i++){
      tmp.push(stack.pop());
      if (tmp.peek().getClass() == String.class && !igStr){
        Object tp = tmp.pop();
        if (CalcFuncs.has((String)tp).bool)
          tmp.push(CalcFuncs.get((String)tp));
        else
          tmp.push(tp);
      }
      if (!((Class<?>)mPar[i]).isAssignableFrom(tmp.peek().getClass())){
        replayStack(tmp);
        return false;
      }
    }
    replayStack(tmp);
    return true;
  }

  private static void replayStack(Stack<Object> tmp){
    while (!tmp.isEmpty())
      stack.push(tmp.pop());
  }

  public static String gets(){
    String res = "";
    try {
      res = in.readLine();
    }
    catch(Exception e){}
    return res;
  }

  public static void puts(String arg){
    System.out.println(arg);
  }

  public static void print(String arg){
    System.out.print(arg);
  }
}
